package heap

import "strings"

type MethodDescriptorParser struct {
	raw    string
	offset int
	parsed *MethodDescriptor
}

// parseMethodDescriptor 解析方法描述符
func parseMethodDescriptor(descriptor string) *MethodDescriptor {
	parser := &MethodDescriptorParser{}
	return parser.parse(descriptor)
}

func (e *MethodDescriptorParser) parse(descriptor string) *MethodDescriptor {
	e.raw = descriptor
	e.parsed = &MethodDescriptor{}
	e.startParams()
	e.parseParamTypes()
	e.endParams()
	e.parseReturnType()
	e.finish()
	return e.parsed
}

func (e *MethodDescriptorParser) startParams() {
	if e.readUint8() != '(' {
		e.causePanic()
	}
}

func (e *MethodDescriptorParser) endParams() {
	if e.readUint8() != ')' {
		e.causePanic()
	}
}

func (e *MethodDescriptorParser) finish() {
	if e.offset != len(e.raw) {
		e.causePanic()
	}
}

// parseParamTypes 解析参数类型
func (e *MethodDescriptorParser) parseParamTypes() {
	for {
		fieldType := e.parseFieldType()
		if fieldType != "" {
			e.parsed.addParameterType(fieldType)
		} else {
			break
		}
	}
}

// parseReturnType 解析返回类型
func (e *MethodDescriptorParser) parseReturnType() {
	if e.readUint8() == 'V' {
		e.parsed.returnType = "V"
		return
	}

	e.unreadUint8()
	fieldType := e.parseFieldType()
	if fieldType != "" {
		e.parsed.returnType = fieldType
		return
	}

	e.causePanic()
}

func (e *MethodDescriptorParser) readUint8() uint8 {
	b := e.raw[e.offset]
	e.offset++
	return b
}

func (e *MethodDescriptorParser) unreadUint8() {
	e.offset--
}

func (e *MethodDescriptorParser) causePanic() {
	panic("BAD descriptor:" + e.raw)
}

// parseFieldType 解析属性类型
func (e *MethodDescriptorParser) parseFieldType() string {
	switch e.readUint8() {
	case 'B':
		return "B"
	case 'C':
		return "C"
	case 'D':
		return "D"
	case 'F':
		return "F"
	case 'I':
		return "I"
	case 'J':
		return "J"
	case 'S':
		return "S"
	case 'Z':
		return "Z"
	case 'L':
		return e.parseObjectType()
	case '[':
		return e.parseArrayType()
	default:
		e.unreadUint8()
		return ""
	}
}

// parseObjectType 解析类型
func (e *MethodDescriptorParser) parseObjectType() string {
	unread := e.raw[e.offset:]
	semicolonIndex := strings.IndexRune(unread, ';')
	if semicolonIndex == -1 {
		e.causePanic()
		return ""
	} else {
		objStart := e.offset - 1
		objEnd := e.offset + semicolonIndex + 1
		e.offset = objEnd
		descriptor := e.raw[objStart:objEnd]
		return descriptor
	}
}

// parseArrayType 解析数组
func (e *MethodDescriptorParser) parseArrayType() string {
	arrStart := e.offset - 1
	e.parseFieldType()
	arrEnd := e.offset
	descriptor := e.raw[arrStart:arrEnd]
	return descriptor
}
